/* celp_fxp.c */

/**********************/
/* CELP-CODEC (FIXED) */
/* for Secure-Phone   */
/* Version 1.01,20.12 */
/* (C) 2002 by NAH6   */
/**********************/

  // how to profile this code:
  //
  // uncomment the following line, and link with profiling enabled codec
  //     ( compiled with '-pg' and without '-fomit-frame-pointer' )

  //  #define _USE_PROFILER

  // make sure you don't compile the profiling code with profiling enabled!!
  // the profiling code consists of 2 objects: mcount.o and mcountinit.o

  // profiling information is gathered while using the codec ( = making a call )
  //
  //  to get the profiling data, the 'secure phone.exe' app must be restarted,
  //  (this makes the machine call codec_enc_destroy, which then writes \gmon.out )
  //
  //  a perl script exists (gmon.pl) which reads the profiling data, combines it with
  //  the corresponding map file, and outputs percentages of times spent in all functions.


/*------------*/
/*  INCLUDES  */
/*------------*/

#include <windows.h>

#include "celp_fxp.h"

/*-----------*/
/*  DEFINES  */
/*-----------*/

#define CELP_FRAME_LENGTH   (240)
#define CELP_WIREREP_BITS   (144)
#define CELP_WIREREP_BYTES   (18)

/*--------------*/
/*  PROTOTYPES  */
/*--------------*/

extern void celp_init   (void);
extern void celp_encode (unsigned char *data, const signed short *audio);
extern void celp_decode (signed short *audio, const unsigned char *data);

extern void ACBGainEncode (void);

/*-------------*/
/*  VARIABLEN  */
/*-------------*/

static HINSTANCE codec_inst;

static LONG      codec_enc_free;
static int       codec_enc_frame;

static LONG      codec_dec_free;
static int       codec_dec_frame;

/**************************************************************************
*                                                                         *
*       U.S. Department of Defense                                        *
*       CELP Voice Coder                                                  *
*       Version 3.3c                                                      *
*       1 June 1994                                                       *
*                                                                         *
*       FOR OFFICIAL U.S. GOVERNMENT CONTRACT USE ONLY                    *
*                                                                         *
*       The U.S. Government shall not be held liable for any damages      *
*       resulting from this code.  Further reproduction or distribution   *
*       of this code without prior written permission of the U.S.         *
*       Government is prohibited.  R224 may be contacted at:              *
*                                                                         *
*               DIRNSA, R224                                              *
*               9800 Savage Road                                          *
*               Fort Meade, MD 20755-6000 USA                             *
*                                                                         *
*==========================================================================
*
* REFERENCES
*
*       "Details to Assist in Implementation of Federal Standard 1016 CELP,"
*       Technical Information Bulletin 92-1, National Communications System,
*       (included as a Postscript file in CELP 3.3 Release)
*
*       "Federal Standard 1016," Telecommunications: Analog-to-Digital
*       Conversion of Voice by 4800 bit/second Code Excited Linear
*       Predictive (CELP) Coding, National Communications System, distributed
*       by the General Services Administration:
*           GSA Federal Supply Service Bureau
*           Specification Section, Suite 8100
*           470 E. L'Enfant Place, S.W.
*           Washington, DC 20407
*           (202)755-0325
*
*      Misc: The following articles describe the Federal-Standard-1016
*       4.8-kbps CELP coder (it's unnecessary to read more than one):
*          + Campbell, Joseph P. Jr., Thomas E. Tremain and Vanoy C.
*            Welch, "The Federal Standard 1016 4800 bps CELP Voice Coder,"
*            Digital Signal Processing, Academic Press, 1991, Vol. 1, No.
*            3, p. 145-155.
*          + Campbell, Joseph P. Jr., Thomas E. Tremain and Vanoy C.
*            Welch, "The DoD 4.8 kbps Standard (Proposed Federal Standard
*            1016)," in Advances in Speech Coding, ed. Atal, Cuperman and
*            Gersho, Kluwer Academic Publishers, 1991, Chapter 12, p.
*            121-133.
*          + Campbell, Joseph P. Jr., Thomas E. Tremain and Vanoy C.
*            Welch, "The Proposed Federal Standard 1016 4800 bps Voice
*            Coder: CELP," Speech Technology Magazine, April/May 1990, p.
*            58-64.
*
*
*
*******************************************************************************/

/*-------------*/
/*  WIN-ENTRY  */
/*-------------*/
                                                      /* Windows-Entry [4200] */
BOOL APIENTRY DllMain (HINSTANCE inst, DWORD reason, LPVOID reserved)
{
  BOOL b;

  if (reason == DLL_PROCESS_ATTACH)
  {
    b = DisableThreadLibraryCalls ((HMODULE)inst);
    if (!b)
      return FALSE;
    codec_inst = inst;
    codec_enc_free = 1L;
    codec_dec_free = 1L;
  }
  else if (reason == DLL_PROCESS_DETACH)
  {
    codec_inst = NULL;
    codec_enc_free = 0L;
    codec_dec_free = 0L;
  }
  return TRUE;
}

/*----*/

void abort (void)                                              /* Codec-Abort */
{
  RaiseException (0xFFFFFFFFL, EXCEPTION_NONCONTINUABLE, 0L, NULL);
  while (42) {}
}

/*-------------*/
/*  API-CALLS  */
/*-------------*/
                                                     /* Get Codec-Info [4201] */
EXPORT signed short CALLBACK codec_getinfo (struct codec_info *info)
{
  /* Check pointer */
  if (info == NULL)
    return E_CELP_FXP_NULLPTR;

  /* Write info */
  FillMemory (info, sizeof (struct codec_info), 0x00);
  info->magic = K_CELP_FXP_MAGIC;
  info->ifver = K_CELP_FXP_IFVER;
  info->ifrev = K_CELP_FXP_IFREV;
  info->ifopt = K_CELP_FXP_IFOPT;
  info->enc_mask[0] = 0x40;
  info->dec_mask[0] = 0x40;
  info->code = (void *)ACBGainEncode;
  return 0;
}

/*----*/
                                                       /* Get Revision [4202] */
EXPORT signed short CALLBACK codec_getrev (unsigned char codec)
{
  /* Check codec */
  if (codec != K_CELP_FXP_ID0)
    return E_CELP_FXP_CODEC;

  /* Return revision */
  return K_CELP_FXP_REV0;
}

/*----------------------------------------------------------------------------*/
#ifdef _USE_PROFILER
void monexit();
void monstartup(int ndepth, int nentries);
int mon_initialized= 0;
#endif

#ifdef DEBUG_CELP
FILE *f_audioin;
FILE *f_audioout;
FILE *f_celpin;
FILE *f_celpout;
#endif
/*-----------*/
/*  ENCODER  */
/*-----------*/
                                                       /* Open Encoder [4210] */
EXPORT signed short CALLBACK codec_enc_create (unsigned char codec,            \
                                               void **state)
{
  LONG d;

  /* Check arguments */
  if (state == NULL)
    return E_CELP_FXP_NULLPTR;
  if (codec != K_CELP_FXP_ID0)
    return E_CELP_FXP_CODEC;

  /* Check unused */
  d = InterlockedExchange (&codec_enc_free, 0L);
  if (!d)
    return E_CELP_FXP_INUSE;

  /* Init state-ptr */
  codec_enc_frame = 1;
  *state = &codec_enc_frame;

#ifdef DEBUG_CELP
  f_audioin= fopen("audioin.raw", "w+b");
  f_celpout= fopen("celpout.raw", "w+b");
#endif

#ifdef _USE_PROFILER
  if (!mon_initialized) {
	monstartup(64, 1024);
	mon_initialized= 1;
  }
#endif

  /* Init celp */
  celp_init ();
  return 0;
}

/*----*/
                                                      /* Close Encoder [4211] */
EXPORT signed short CALLBACK codec_enc_destroy (unsigned char codec,           \
                                                void *state)
{
  /* Check arguments */
  if (state == NULL)
    return E_CELP_FXP_NULLPTR;
  if (codec != K_CELP_FXP_ID0)
    return E_CELP_FXP_CODEC;

#ifdef _USE_PROFILER
  if (mon_initialized) {
	monexit();
	mon_initialized= 0;
  }
#endif

#ifdef DEBUG_CELP
  fclose(f_audioin);
  fclose(f_celpout);
#endif

  /* Free encoder */
  codec_enc_free = 1L;
  return 0;
}

/*====*/
                                                     /* Compress Audio [4212] */
EXPORT signed short CALLBACK codec_enc_data (unsigned char codec, void *state, \
                                             const signed short *audio,        \
                                             unsigned char *data,              \
                                             unsigned long *alen,              \
                                             unsigned long *dlen)
{
  unsigned long a,d;

  /* Check arguments */
  if ((state == NULL) || (audio == NULL) || (data == NULL) \
   || (alen == NULL)  || (dlen == NULL))
    return E_CELP_FXP_NULLPTR;
  if (codec != K_CELP_FXP_ID0)
    return E_CELP_FXP_CODEC;

  /* Convert data */
  a = *alen;
  d = *dlen;
  while ((a >= CELP_FRAME_LENGTH) && (d >= CELP_WIREREP_BYTES))
  {
#ifdef DEBUG_CELP
	fwrite(audio, sizeof(short), CELP_FRAME_LENGTH, f_audioin);
#endif
    /* Compress frame */
    celp_encode (data, audio);
#ifdef DEBUG_CELP
	fwrite(data, 1, CELP_WIREREP_BYTES, f_celpout);
#endif

    audio += CELP_FRAME_LENGTH;
    data  += CELP_WIREREP_BYTES;
    a -= CELP_FRAME_LENGTH;
    d -= CELP_WIREREP_BYTES;
    codec_enc_frame++;
  }
  *alen = *alen - a;
  *dlen = *dlen - d;
  return 0;
}

/*----------------------------------------------------------------------------*/

/*-----------*/
/*  DECODER  */
/*-----------*/
                                                       /* Open Decoder [4220] */
EXPORT signed short CALLBACK codec_dec_create (unsigned char codec,            \
                                               void **state)
{
  LONG d;

  /* Check arguments */
  if (state == NULL)
    return E_CELP_FXP_NULLPTR;
  if (codec != K_CELP_FXP_ID0)
    return E_CELP_FXP_CODEC;

  /* Check unused */
  d = InterlockedExchange (&codec_dec_free, 0L);
  if (!d)
    return E_CELP_FXP_INUSE;

  /* Init state-ptr */
  codec_dec_frame = 1;
  *state = &codec_dec_frame;

#ifdef DEBUG_CELP
  f_audioout= fopen("audioout.raw", "w+b");
  f_celpin= fopen("celpin.raw", "w+b");
#endif

#ifdef _USE_PROFILER
  if (!mon_initialized) {
	monstartup(64, 1024);
	mon_initialized= 1;
  }
#endif

  /* Init celp */
  celp_init ();
  return 0;
}

/*----*/
                                                      /* Close Decoder [4221] */
EXPORT signed short CALLBACK codec_dec_destroy (unsigned char codec,           \
                                                void *state)
{
  /* Check arguments */
  if (state == NULL)
    return E_CELP_FXP_NULLPTR;
  if (codec != K_CELP_FXP_ID0)
    return E_CELP_FXP_CODEC;

#ifdef DEBUG_CELP
  fclose(f_audioout);
  fclose(f_celpin);
#endif

#ifdef _USE_PROFILER
  if (mon_initialized) {
	monexit();
	mon_initialized= 0;
  }
#endif

  /* Free decoder */
  codec_dec_free = 1L;
  return 0;
}

/*====*/
                                                       /* Expand Audio [4222] */
EXPORT signed short CALLBACK codec_dec_data (unsigned char codec, void *state, \
                                             const unsigned char *data,        \
                                             signed short *audio,              \
                                             unsigned long *dlen,              \
                                             unsigned long *alen)
{
  unsigned long a,d;

  /* Check arguments */
  if ((state == NULL) || (data == NULL) || (audio == NULL) \
   || (dlen == NULL)  || (alen == NULL))
    return E_CELP_FXP_NULLPTR;
  if (codec != K_CELP_FXP_ID0)
    return E_CELP_FXP_CODEC;

  /* Convert data */
  d = *dlen;
  a = *alen;
  while ((d >= CELP_WIREREP_BYTES) && (a >= CELP_FRAME_LENGTH))
  {
#ifdef DEBUG_CELP
	fwrite(data, 1, CELP_WIREREP_BYTES, f_celpin);
#endif
    /* Expand frame */
    celp_decode (audio, data);
#ifdef DEBUG_CELP
	fwrite(audio, sizeof(short), CELP_FRAME_LENGTH, f_audioout);
#endif

    data  += CELP_WIREREP_BYTES;
    audio += CELP_FRAME_LENGTH;
    d -= CELP_WIREREP_BYTES;
    a -= CELP_FRAME_LENGTH;
    codec_dec_frame++;
  }
  *dlen = *dlen - d;
  *alen = *alen - a;
  return 0;
}


/*** EOF ***/
